/*
 * Copyright 2014, NuzoweR@Schneefeuer <nuzower@gmail.com>.
 * The Settlers Online - Combat Simulator (tsosim).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "CombatLog.h"
#include "Garrison.h"
#include "Utils.h"

#include <iostream>
#include <iomanip>


CombatLog::UnitAttackData::UnitAttackData(const Unit* _attacker, int _att_number, int _damage, const Unit* _defender, int _def_number)
: attacker(_attacker),
  defender(_defender),
  att_unit_number(_att_number),
  def_unit_number(_def_number),
  damage_dealt(_damage),
  def_hitpoints_left(0)
{};


void CombatLog::clear()
{
	current_attack_data = NULL;
	current_group_attack_data = NULL;
	Log.clear();
}

void CombatLog::start_attack(int round, int initiative, const Garrison* attacker, const Garrison* defender)
{
	std::list<AttackData>& lad = Log[round][initiative];
	lad.push_back(AttackData());

	AttackData& AD = lad.back();
	AD.attacker = attacker;
	AD.defender = defender;

	this->current_attack_data = &AD;
}

void CombatLog::add_group_attack(const Garrison::Group* attacker, const Garrison::Group* defender, int num_attackers, int num_defenders)
{
	if(attacker == NULL || defender == NULL)
		return;

	if(current_attack_data == NULL)
		return;

	this->current_attack_data->attack_data.push_back(GroupAttackData());
	current_group_attack_data = &current_attack_data->attack_data.back();

	current_group_attack_data->attacker = attacker;
	current_group_attack_data->defender = defender;
	current_group_attack_data->num_attacker = num_attackers;
	current_group_attack_data->num_defender = num_defenders;

	current_group_attack_data->total_num_attacker = attacker->number;
	current_group_attack_data->total_num_defender = defender->number;

}

void CombatLog::finish_group_attack(int num_defenders_left)
{
	if(current_group_attack_data)
		current_group_attack_data->num_defender_left = num_defenders_left;

	current_group_attack_data = NULL;
}

void CombatLog::add_unit_attack(const Unit* attacker, const Unit* defender, int att_num, int def_num, int damage, int def_hitpoints_left)
{
	if(attacker == NULL || defender == NULL) return;

	if(current_group_attack_data == NULL) return;

	current_group_attack_data->attack_data.push_back(UnitAttackData());
	UnitAttackData& uad = current_group_attack_data->attack_data.back();

	uad.attacker        = attacker;
	uad.defender        = defender;
	uad.att_unit_number = att_num;
	uad.def_unit_number = def_num;
	uad.damage_dealt    = damage;
	uad.def_hitpoints_left = def_hitpoints_left;
}


static const char* indent(int level) {
	switch(level)
	{
	case 1: return "  ";
	case 2: return "    ";
	case 3: return "      ";
	case 4: return "        ";
	case 5: return "          ";
	default : 
		return "";
	}
	//switch(level)
	//{
	//case 1: return "\t";
	//case 2: return "\t\t";
	//case 3: return "\t\t\t";
	//case 4: return "\t\t\t\t";
	//case 5: return "\t\t\t\t\t";
	//default : 
	//	return "";
	//}
}

static inline const char* brace_in(bool on) {
	return on ? "{" : "";
}

static inline const char* brace_out(bool on) {
	return on ? "}" : "";
}


void CombatLog::print_combat_log(std::ostream& out, int log_level, bool print_braces)
{
	bool B = print_braces;

	if(log_level < 1) return;

	out << "LOG :" << brace_in(B) << std::endl;

	AttackByRound_t::const_iterator cit_round = Log.begin();
	for(; cit_round != Log.end(); ++cit_round) 
	{
		out << indent(1) << "Round " << cit_round->first << " " << brace_in(B) << std::endl;
	
		if(log_level < 2) continue;

		AttackByInitiative_t::const_iterator cit_init = cit_round->second.begin();
		for(; cit_init != cit_round->second.end(); ++cit_init)
		{
			out << indent(2) << "Initiative - " << cit_init->first << " " << brace_in(B) << std::endl;
			
			if(log_level < 3) continue;

			std::list<AttackData>::const_iterator cit = cit_init->second.begin();
			for(; cit != cit_init->second.end(); ++cit)
			{

				AttackData const& AD = *cit;

				out << indent(3)
					<< AD.attacker->name()
					<< "  ->  "
					<< AD.defender->name()
					<< " " << brace_in(B) << std::endl;

				if(log_level >= 4) 
				{
					print_group_attack(out, cit->attack_data, log_level, B);
				}
				if(B) out << indent(3) << brace_out(B);
				out << std::endl;
			}
			if(B) out << indent(2) << brace_out(B) << std::endl;
		}
		if(B) out << indent(1) << brace_out(B) << std::endl;
	}
	if(B) out << brace_out(B);
	out << std::endl;
}

void CombatLog::print_group_attack(std::ostream& out, std::list<GroupAttackData> const& lgad, int log_level, bool braces)
{
	int att_name_width=0, def_name_width=0, att_max_units=0, def_max_units=0, def_max_lost=0;
	std::list<GroupAttackData>::const_iterator git = lgad.begin();
	for(; git != lgad.end(); ++git)
	{
		att_name_width = std::max(att_name_width, (int)git->attacker->unit->name.length());
		def_name_width = std::max(def_name_width, (int)git->defender->unit->name.length());
		att_max_units  = std::max(att_max_units, utils::num_width(git->total_num_attacker));
		def_max_units  = std::max(def_max_units, utils::num_width(git->total_num_defender));
	}

	for(git = lgad.begin(); git != lgad.end(); ++git)
	{
		out << indent(4)
			<< "Attack : "
			<< std::setw(att_name_width) << git->attacker->unit->name
			<< " [" << std::setw(att_max_units) << git->attack_data.size() << "/" << std::setw(att_max_units) << git->total_num_attacker << "]"
			<< "  ->  "
			<< std::setw(def_name_width) << git->defender->unit->name
			<< " [ "
			<< std::setw(def_max_units) << git->num_defender_left << "/" << std::setw(def_max_units) << git->total_num_defender;

		int units_died = git->num_defender_left - git->num_defender;
		if(units_died < 0) {
			out << ", " << std::setw(4) << units_died;
		} 
		out << " ] " << brace_in(braces) << std::endl;

		if(log_level >= 5) {
			print_unit_attack(out, git->attack_data, git->num_attacker, git->num_defender);
		}

		if(braces) out << indent(4) << brace_out(braces) << std::endl;
	}
}

void CombatLog::print_unit_attack(std::ostream& out, std::list<UnitAttackData> const& luad, int num_attackers, int num_defenders)
{
	int att_width=0, def_width=0, unit_num_width=0, att_num_width, def_num_width, att_md_width, def_hp_width;
	if(!luad.empty())
	{
		UnitAttackData const& ref = luad.front();
		att_width     = ref.attacker->name.length()+2;
		def_width     = ref.defender->name.length()+2;
		att_num_width = utils::num_width(num_attackers);
		def_num_width = utils::num_width(num_defenders);
		att_md_width  = utils::num_width(ref.attacker->max_damage);
		def_hp_width  = utils::num_width(ref.defender->hitpoints);
	}

	std::list<UnitAttackData>::const_iterator uit = luad.begin();
	for(; uit != luad.end(); ++uit)
	{
		out << indent(5)
			<< "(" << std::setw(att_num_width) << uit->att_unit_number << ") "
			<< std::setw(att_width) << uit->attacker->name 
			<< "   ->   "
			<< "(" << std::setw(def_num_width) << uit->def_unit_number << ") "
			<< std::setw(def_width) << uit->defender->name 
			<< "  :  "
			<< std::setw(att_md_width) << uit->damage_dealt << " damage , " << std::setw(def_hp_width+1) << uit->def_hitpoints_left << " / " << /*std::setw(def_hp_width) <<*/ uit->defender->hitpoints << " hp"
			<< std::endl;
	}
}
